#include "Driver_I2C.h"

/* SDA Config */
#define I2C_SDA_LOW()  FL_GPIO_ResetOutputPin(GPIOA, FL_GPIO_PIN_12)
#define I2C_SDA_HIGH() FL_GPIO_SetOutputPin(GPIOA, FL_GPIO_PIN_12)
#define I2C_READ_SDA() FL_GPIO_GetInputPin(GPIOA, FL_GPIO_PIN_12)

/* SCL1 Config */
#define I2C_SCL1_LOW()  FL_GPIO_ResetOutputPin(GPIOA, FL_GPIO_PIN_11)
#define I2C_SCL1_HIGH() FL_GPIO_SetOutputPin(GPIOA, FL_GPIO_PIN_11)

/* SCL2 Config */
#define I2C_SCL2_LOW()  FL_GPIO_ResetOutputPin(GPIOB, FL_GPIO_PIN_12)
#define I2C_SCL2_HIGH() FL_GPIO_SetOutputPin(GPIOB, FL_GPIO_PIN_12)

/* SCL3 Config */
#define I2C_SCL3_LOW()  FL_GPIO_ResetOutputPin(GPIOB, FL_GPIO_PIN_13)
#define I2C_SCL3_HIGH() FL_GPIO_SetOutputPin(GPIOB, FL_GPIO_PIN_13)

/* I2C Delay */
#if FreeRTOS
#define __I2C_DELAY__() vTaskDelay(5)
#else
#define __I2C_DELAY__() FL_DelayMs(5)
#endif

static void Driver_I2C_SDA2Input(void);
static void Driver_I2C_SDA2Output(void);
static void Driver_I2C_SCLxHigh(void);
static void Driver_I2C_SCLxLow(void);

/* 当前选中的I2C的SCL引脚 */
static SelSCLx_t i2cSCLx = SCLx_1;

/**
 * @brief 初始化I2C
 *
 */
void Driver_I2C_Init(void)
{
    FL_GPIO_InitTypeDef gpioInit = { 0 };

    /* ------------------- SCL、SDA Config ------------------- */
    /*  PA11-SCL1, PA12-SDA */
    gpioInit.pin          = FL_GPIO_PIN_11 | FL_GPIO_PIN_12;
    gpioInit.mode         = FL_GPIO_MODE_OUTPUT;
    gpioInit.outputType   = FL_GPIO_OUTPUT_OPENDRAIN;
    gpioInit.pull         = FL_ENABLE;
    gpioInit.remapPin     = FL_DISABLE;
    gpioInit.analogSwitch = FL_DISABLE;
    FL_GPIO_Init(GPIOA, &gpioInit);

    /* PB12-SCL2, PB13-SCL3 */
    gpioInit.pin = FL_GPIO_PIN_12 | FL_GPIO_PIN_13;
    FL_GPIO_Init(GPIOB, &gpioInit);

    /* ------------------- SSx Config ------------------- */
    /* PA10-SS1 */
    gpioInit.pin          = FL_GPIO_PIN_10;
    gpioInit.mode         = FL_GPIO_MODE_OUTPUT;
    gpioInit.outputType   = FL_GPIO_OUTPUT_PUSHPULL;
    gpioInit.pull         = FL_DISABLE;
    gpioInit.remapPin     = FL_DISABLE;
    gpioInit.analogSwitch = FL_DISABLE;
    FL_GPIO_Init(GPIOA, &gpioInit);

    /* PB0-SS2, PB1-SS3 */
    gpioInit.pin = FL_GPIO_PIN_0 | FL_GPIO_PIN_1;
    FL_GPIO_Init(GPIOB, &gpioInit);

    /* 将SS1、SS2、SS3拉高, 均将CPS121-700配置为I2C模式 */
    FL_GPIO_SetOutputPin(GPIOA, FL_GPIO_PIN_10); // SS1
    FL_GPIO_SetOutputPin(GPIOB, FL_GPIO_PIN_0);  // SS2
    FL_GPIO_SetOutputPin(GPIOB, FL_GPIO_PIN_1);  // SS3
}

/**
 * @brief 发送起始信号
 *
 */
void Driver_I2C_Start(void)
{
    I2C_SDA_HIGH();
    Driver_I2C_SCLxHigh();
    __I2C_DELAY__();

    /* 拉低发送起始信号 */
    I2C_SDA_LOW();
    __I2C_DELAY__();

    /* 按照约定, 最后拉低SCL1 */
    Driver_I2C_SCLxLow();
    __I2C_DELAY__();
}

/**
 * @brief 发送停止信号
 *
 */
void Driver_I2C_Stop(void)
{
    I2C_SDA_LOW();
    __I2C_DELAY__();

    Driver_I2C_SCLxHigh();
    __I2C_DELAY__();

    I2C_SDA_HIGH();
    __I2C_DELAY__();
}

/**
 * @brief 发送ACK
 *
 */
void Driver_I2C_Ack(void)
{
    I2C_SDA_LOW();
    __I2C_DELAY__();

    Driver_I2C_SCLxHigh();
    __I2C_DELAY__();

    Driver_I2C_SCLxLow();
    __I2C_DELAY__();

    I2C_SDA_HIGH();
    __I2C_DELAY__();
}

/**
 * @brief 发送NACK
 *
 */
void Driver_I2C_Nack(void)
{
    I2C_SDA_HIGH();
    __I2C_DELAY__();

    Driver_I2C_SCLxHigh();
    __I2C_DELAY__();

    Driver_I2C_SCLxLow();
    __I2C_DELAY__();
}

/**
 * @brief 等待对方ACK响应
 *
 * @return ACKStatus_t ACK or NACK
 */
ACKStatus_t Driver_I2C_Wait4Ack(void)
{
    I2C_SDA_HIGH();
    __I2C_DELAY__();

    Driver_I2C_SCLxHigh();
    __I2C_DELAY__();

    ACKStatus_t ackStatus = (ACKStatus_t)I2C_READ_SDA();

    Driver_I2C_SCLxLow();
    __I2C_DELAY__();

    return ackStatus;
}

/**
 * @brief 发送一个字节的数据
 *
 * @param txData 发送的一个字节数据
 */
void Driver_I2C_SendByte(uint8_t txData)
{
    for (uint8_t i = 0; i < 8; ++i) {
        if (txData & 0x80) {
            I2C_SDA_HIGH();
        }
        else {
            I2C_SDA_LOW();
        }
        __I2C_DELAY__();

        Driver_I2C_SCLxHigh();
        __I2C_DELAY__();

        Driver_I2C_SCLxLow();
        __I2C_DELAY__();

        txData <<= 1;
    }
}

/**
 * @brief 接收一个字节的数据
 *
 * @return uint8_t 接收到的一个字节的数据
 */
uint8_t Driver_I2C_ReceiveByte(void)
{
    uint8_t rxData = 0;

    I2C_SDA_HIGH(); // 释放SDA总线
    __I2C_DELAY__();

    /* 将SDA设置为输入模式 */
    Driver_I2C_SDA2Input();

    for (uint8_t i = 0; i < 8; ++i) {
        rxData <<= 1;

        Driver_I2C_SCLxHigh();
        __I2C_DELAY__();

        uint8_t bitValue = I2C_READ_SDA();

        if (bitValue) {
            rxData |= 0x01;
        }

        Driver_I2C_SCLxLow();
        __I2C_DELAY__();
    }

    /* 恢复SDA为输出模式 */
    Driver_I2C_SDA2Output();

    return rxData;
}

/**
 * @brief 选择要使用的SCL引脚
 *
 * @param sclx SCLx_1、SCLx_2、SCLx_3
 */
void Driver_I2C_SelectSCLx(SelSCLx_t sclx)
{
    switch (sclx) {
    case SCLx_1: i2cSCLx = SCLx_1; break;
    case SCLx_2: i2cSCLx = SCLx_2; break;
    case SCLx_3: i2cSCLx = SCLx_3;
    }
}

/**
 * @brief 将SDA引脚设置为输入模式
 *
 */
static void Driver_I2C_SDA2Input(void)
{
    FL_GPIO_InitTypeDef gpioInit = { 0 };

    gpioInit.pin          = FL_GPIO_PIN_12; // SDA引脚
    gpioInit.mode         = FL_GPIO_MODE_INPUT;
    gpioInit.outputType   = FL_GPIO_OUTPUT_OPENDRAIN;
    gpioInit.pull         = FL_ENABLE;
    gpioInit.remapPin     = FL_DISABLE;
    gpioInit.analogSwitch = FL_DISABLE;
    FL_GPIO_Init(GPIOA, &gpioInit);
}

/**
 * @brief 将SDA引脚设置为输出模式
 *
 */
static void Driver_I2C_SDA2Output(void)
{
    FL_GPIO_InitTypeDef gpioInit = { 0 };

    gpioInit.pin          = FL_GPIO_PIN_12; // SDA引脚
    gpioInit.mode         = FL_GPIO_MODE_OUTPUT;
    gpioInit.outputType   = FL_GPIO_OUTPUT_OPENDRAIN;
    gpioInit.pull         = FL_ENABLE;
    gpioInit.remapPin     = FL_DISABLE;
    gpioInit.analogSwitch = FL_DISABLE;
    FL_GPIO_Init(GPIOA, &gpioInit);
}

/**
 * @brief 将选中的SCL引脚设置为高电平
 *
 */
static void Driver_I2C_SCLxHigh(void)
{
    switch (i2cSCLx) {
    case SCLx_1: I2C_SCL1_HIGH(); break;
    case SCLx_2: I2C_SCL2_HIGH(); break;
    case SCLx_3: I2C_SCL3_HIGH(); break;
    }
}

/**
 * @brief 将选中的SCL引脚设置为低电平
 *
 */
static void Driver_I2C_SCLxLow(void)
{
    switch (i2cSCLx) {
    case SCLx_1: I2C_SCL1_LOW(); break;
    case SCLx_2: I2C_SCL2_LOW(); break;
    case SCLx_3: I2C_SCL3_LOW(); break;
    }
}
